home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / flockMyworldService.js < prev    next >
Text File  |  2007-10-18  |  19KB  |  602 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16.  
  17. // TODO JVL: queue is now a stack but the syntax continues to use 'queue'.  Will fix later.
  18.  
  19. // XPConnect Helpers
  20. var Cc = Components.classes;
  21. var Ci = Components.interfaces;
  22. var Cr = Components.results;
  23.  
  24. // Constants 
  25. const MYWORLD_SHORTNAME = "myworld";
  26. const MYWORLD_FULLNAME = "Myworld";
  27. const MYWORLD_TITLE = "Myworld Service";
  28. const MYWORLD_FAVICON = "http://www.flock.com/favicon.ico";
  29. const MYWORLD_CID = Components.ID("{bb35349f-fb23-3089-4d3d-28462e1c98eb}");
  30. const MYWORLD_CONTRACTID = "@flock.com/myworld-service;1";
  31. const CATEGORY_COMPONENT_NAME = "Myworld JS Component";
  32.  
  33. const CACHESERVICE_CONTRACTID = "@mozilla.org/network/cache-service;1";
  34. const TIMER_CONTRACTID = "@mozilla.org/timer;1";
  35. const XMLHTTPREQUEST_CONTRACTID = "@mozilla.org/xmlextras/xmlhttprequest;1";
  36.  
  37. const MAX_FLICKR_FARMS = 10;
  38. const PROTOCOL = "http://";
  39. const FLICKR_FARM = "farm";
  40. const MAX_RETRIEVAL_ATTEMPTS = 3;
  41. const DEFAULT_INTERVAL = 500;  // in milliseconds
  42. const DEFAULT_TIMER_TYPE = Ci.nsITimer.TYPE_REPEATING_PRECISE;
  43.  
  44.  
  45.  
  46. // Component Utils (just used for the timer/scheduler)
  47. var gCompTK;
  48. function getCompTK() {
  49.   if (!gCompTK) {
  50.     var gCompTK = Cc["@flock.com/singleton;1"]
  51.       .getService(Ci.flockISingleton)
  52.       .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
  53.       .wrappedJSObject;
  54.   }
  55.   return gCompTK;
  56. }
  57.  
  58.  
  59.  
  60. // ===========================================================
  61. // ========== BEGIN flockMyworldService Component ============
  62. // ===========================================================
  63.  
  64.  
  65. // BEGIN Constructor
  66. function myworldService()
  67. {
  68.   this._logger = Cc["@flock.com/logger;1"].createInstance(Ci.flockILogger);
  69.   this._logger.init("MyworldService");
  70.  
  71.   this._logger.debug("BEGIN Ctor");
  72.   this._queue = [];
  73.  
  74.   this._ctk = {
  75.     interfaces: [
  76.       "nsISupports",
  77.       "nsIClassInfo",
  78.       "nsISupportsCString",
  79.       "nsIObserver",
  80.       "flockIMyworldService"
  81.     ],
  82.     shortName: MYWORLD_SHORTNAME,
  83.     fullName: MYWORLD_FULLNAME,
  84.     description: MYWORLD_TITLE,
  85.     favicon: MYWORLD_FAVICON,
  86.     CID: MYWORLD_CID,
  87.     contractID: MYWORLD_CONTRACTID
  88.   };
  89.  
  90.   this.obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  91.   this.obs.addObserver(this, "xpcom-shutdown", false);
  92.   this.obs.addObserver(this, "flock-data-ready", false);
  93.  
  94.   this._coop = Cc["@flock.com/singleton;1"]
  95.                .getService(Ci.flockISingleton)
  96.                .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  97.                .wrappedJSObject;
  98.  
  99.   var inst = this;  // this is necessary as you cannot use 'this' within the defined notify function
  100.   var callback = { notify : function(aTimer) { inst.processQueue(); } };
  101.   this._timer = Components.classes[TIMER_CONTRACTID].createInstance(Ci.nsITimer);
  102.   this._timer.initWithCallback(callback, DEFAULT_INTERVAL, DEFAULT_TIMER_TYPE);
  103.  
  104.   this._logger.info("Myworld service created");
  105.   this._logger.debug("END Ctor");
  106. }
  107. // END Constructor
  108.  
  109. myworldService.prototype.init =
  110. function myworldService_init()
  111. {
  112.   var loosePhotosEnum = this._coop.RichPhoto.all();
  113.   while (loosePhotosEnum.hasMoreElements()) {
  114.     var photo = loosePhotosEnum.getNext();
  115.     if (!photo.cachedThumbnail || photo.cachedThumbnail == "") {
  116.       this.queueResource(photo.id());
  117.     }
  118.   }
  119. }
  120.  
  121. // BEGIN nsIObserver
  122. myworldService.prototype.observe = function (subject, topic, data)
  123. {
  124.   if (topic == "xpcom-shutdown")
  125.   {
  126.     this.obs.removeObserver(this, "xpcom-shutdown");
  127.   } else if (topic == "flock-data-ready") {
  128.     this.obs.removeObserver(this, "flock-data-ready");
  129.     this.init();
  130.   }
  131. }
  132. // END nsIObserver
  133.  
  134.  
  135. // BEGIN flockIMyworldService
  136. myworldService.prototype.flags = Ci.nsIClassInfo.SINGLETON;
  137.  
  138. // queue a unique resource for processing
  139. myworldService.prototype.queueResource =
  140. function myworldService_queueResource(aURN)
  141. {
  142.   this._logger.debug("BEGIN queueResource: " + aURN);
  143.   if (!this.queueResourceExists(aURN))
  144.   {
  145.     this.createQueueResource(aURN);
  146.   }
  147.   this._logger.debug("END queueResource: " + aURN);
  148. }
  149.  
  150.  
  151. // clear the queue
  152. myworldService.prototype.clearQueue =
  153. function myworldService_clearQueue()
  154. {
  155.   while(this._queue.length > 0)
  156.   {
  157.     this._queue.shift();
  158.   }
  159.   this._logger.info("queue cleared");
  160. }
  161.  
  162.  
  163. // check the browser cache for the given key
  164. // returns a valid cached key or null if not found
  165. myworldService.prototype.checkCache = 
  166. function myworldService_checkCache(aKey)
  167. {
  168.   this._logger.debug("BEGIN checkCache: " + aKey);
  169.   if (aKey)
  170.   {
  171.     var cacheSession = this.getCacheSession();
  172.     if (cacheSession)
  173.     {
  174.       // check in the browser cache for the given key
  175.       if (this.isValidCacheEntry(cacheSession, aKey))
  176.       {
  177.         this._logger.debug("END checkCache: " + aKey);
  178.         return aKey;
  179.       }
  180.  
  181.       // we couldn't find the given resource in the browser cache, it may have been forwarded to a farm...
  182.       // trim the url(key) for our search needs and search for this modified url(key)
  183.       var keyBase = aKey;
  184.       if (aKey.indexOf(PROTOCOL) == 0)
  185.       {
  186.         keyBase = aKey.substr(PROTOCOL.length);
  187.       }
  188.  
  189.       for (var idx = 1; idx < MAX_FLICKR_FARMS + 1; idx++)
  190.       {
  191.         var key = PROTOCOL + FLICKR_FARM + idx + "." + keyBase;
  192.         if (this.isValidCacheEntry(cacheSession, key))
  193.         {
  194.           this._logger.debug("END checkCache: " + key);
  195.           return key;
  196.         }
  197.       }
  198.     }
  199.   }
  200.  
  201.   this._logger.debug("END checkCache: null");
  202.   return null;
  203. }
  204. // END flockIMyworldService
  205.  
  206.  
  207. // BEGIN flockMyworldService
  208. // returns the cache session or null if an error occured
  209. myworldService.prototype.getCacheSession =
  210. function myworldService_getCacheSession()
  211. {
  212.   var cacheService = Cc[CACHESERVICE_CONTRACTID].getService(Ci.nsICacheService);
  213.   if (!cacheService)
  214.   {
  215.     this._logger.debug("getCacheSession: No cache service");
  216.     return null;
  217.   }
  218.  
  219.   // check both memory and disk cache
  220.   // NOTE: 'HTTP' is the name for the browser cache
  221.   var cacheSession = cacheService.createSession("HTTP", Ci.nsICache.STORE_ANYWHERE, true);
  222.   if (!cacheSession)
  223.   {
  224.     this._logger.debug("getCacheSession: No cache session");
  225.     return null;
  226.   }
  227.  
  228.   cacheSession.doomEntriesIfExpired = false;
  229.   return cacheSession;
  230. }
  231.  
  232.  
  233. // check that the given key is in the browser cache
  234. // returns true if found, false otherwise
  235. myworldService.prototype.isValidCacheEntry =
  236. function isValidCacheEntry(cacheSession, aKey)
  237. {
  238.   this._logger.debug("BEGIN isValidCacheEntry: " + aKey);
  239.   if (!cacheSession)
  240.   {
  241.     this._logger.debug("END isValidCacheEntry: No cache session");
  242.     return false;
  243.   }
  244.  
  245.   if (!aKey || aKey.length < 1)
  246.   {
  247.     this._logger.debug("END isValidCacheEntry: No key supplied");
  248.     return false;
  249.   }
  250.  
  251.   try
  252.   {
  253.     // checks browser cache for given key(url)
  254.     // found if a cacheEntryDescriptor is returned and the resource's size is > 0,
  255.     // otherwise it will throw an exception
  256.     var cacheEntryDescriptor = cacheSession.openCacheEntry(aKey, Ci.nsICache.ACCESS_READ, false);
  257.     var found = this.isNonEmptyResource(cacheEntryDescriptor);
  258.     var msg = "END isValidCacheEntry: openCacheEntry: " + aKey + (found ? " (found in cache)" : " (not found in cache)");
  259.     this._logger.debug(msg);
  260.     return found;
  261.   }
  262.   catch (ex)
  263.   {
  264.     this._logger.debug("isValidCacheEntry: " + aKey + " (not in cache)");
  265.   }
  266.  
  267.   this._logger.debug("END isValidCacheEntry: false");
  268.   return false;
  269. }
  270.  
  271.  
  272. // helper function to determine if the cacheEntryDescriptor is:
  273. // 1) not null
  274. // 2) size > 0
  275. // also reports additional details if showDetails = true
  276. // returns true if it passes the above criteria, false otherwise
  277. myworldService.prototype.isNonEmptyResource =
  278. function isNonEmptyResource(cacheEntryDescriptor, showDetails)
  279. {
  280.   if (showDetails)
  281.     this._logger.debug("isNonEmptyResource:");
  282.  
  283.   // NOTE: when checking cacheEntryDescriptor for nullness, we need to check it explictly against null.  '!cacheEntryDescriptor' will not work!
  284.   var found = (cacheEntryDescriptor != null);
  285.  
  286.   if (showDetails)
  287.     this._logger.debug("    descriptor: " + (found ? "not null" : "null"));
  288.  
  289.   var size = cacheEntryDescriptor.dataSize;
  290.  
  291.   if (showDetails)
  292.     this._logger.debug("    size: " + size);
  293.  
  294.   return found && (size > 0);
  295. }
  296.  
  297.  
  298. // process the elements stored in the queue
  299. myworldService.prototype.processQueue =
  300. function processQueue()
  301. {
  302.   var elem = null;
  303.   var coopObj = null;
  304.   var thumbnail = null;
  305.  
  306.   while (1)
  307.   {
  308.     //this._logger.debug("BEGIN processQueue: current queue size: " + this._queue.length);
  309.     if (this._queue.length < 1)
  310.     {
  311.       //this._logger.debug("END processQueue: nothing in the queue to process");
  312.       return;
  313.     }
  314.  
  315.     // pop the first entry off the queue for processing
  316.     elem = this._queue.shift();
  317.     if (!elem)
  318.     {
  319.       this._logger.debug("END processQueue: element popped off queue is null, nothing to process");
  320.       return;
  321.     }
  322.  
  323.     this._logger.debug("processQueue:");
  324.     this._logger.debug("    processing: " + elem.urn);
  325.     this._logger.debug("    items left in queue: " + this._queue.length);
  326.     
  327.     // retrieve the appropriate resource from the rdf in memory
  328.     coopObj = this._coop.get(elem.urn);
  329.     if (!coopObj)
  330.     {
  331.       this._logger.debug("END processQueue: " + elem.urn + " (not in rdf)");
  332.       return;
  333.     }
  334.  
  335.     thumbnail = coopObj.thumbnail;
  336.     this._logger.debug("processQueue: thumbnail: " + thumbnail);
  337.  
  338.     // if the retrieved resource doesn't have topmedia as a parent,
  339.     // discard from the queue and just set the regular thumbnail in hopes it can show something
  340.     if (this.belongsToTopMedia(coopObj))
  341.     {
  342.       this._logger.debug("processQueue: " + elem.urn + " (found in topmedia)");
  343.       break;
  344.     }
  345.     else
  346.     {
  347.       this._logger.debug("processQueue: " + elem.urn + " (not in topmedia, process the next item in the queue)");
  348.       coopObj.cachedThumbnail = thumbnail;
  349.     }
  350.   }
  351.  
  352.   // we only need to update the rdf with the cachedThumbnail if it doesn't already exist
  353.   if (!this.isValidCachedThumbnail(coopObj.cachedThumbnail, thumbnail))
  354.   {
  355.     // take a look in the browser cache for the cached thumbnail url
  356.     thumbnail = this.checkCache(thumbnail);
  357.     if (thumbnail)
  358.     {
  359.       this._logger.debug("processQueue: " + thumbnail + " (in cache)");
  360.       
  361.       // found the cached thumbnail url, now store it in the rdf; we're done!
  362.       coopObj.cachedThumbnail = thumbnail;
  363.     }
  364.     else
  365.     {
  366.       this._logger.debug("processQueue: " + coopObj.thumbnail + " (not in cache)");
  367.       coopObj.cachedThumbnail = "";
  368.       
  369.       // we couldn't find the cached thumbnail url in the browser cache so retrieve the resource
  370.       // from the social service to see if we can cache it ourselves and retrieve it later
  371.       this.reRequestResource(coopObj.thumbnail);
  372.  
  373.       this._logger.debug("processQueue: re-add to queue:");
  374.       this._logger.debug("    URN: " + elem.urn);
  375.       this._logger.debug("    current attempt: " + elem.attempt);
  376.  
  377.       // since it wasn't in the browser cache this time, re-add the queued entry to the end of the
  378.       // queue; we will try retrieving it later (for a maximum of MAX_RETRIEVAL_ATTEMPTS) otherwise,
  379.       // just fail as there might be something wrong with the social service itself
  380.       this.requeueResource(elem, coopObj)
  381.     }
  382.   }
  383.  
  384.   this._logger.debug("END processQueue");
  385. }
  386.  
  387.  
  388. // checks to see if the coop object has a topmedia as a parent
  389. // returns true if topmedia is a parent, false otherwise
  390. myworldService.prototype.belongsToTopMedia =
  391. function myworldService_belongsToTopMedia(coopObj)
  392. {
  393.   if (!coopObj)
  394.   {
  395.     return false;
  396.   }
  397.  
  398.   var parents = coopObj.getParents();
  399.   if (!parents)
  400.   {
  401.     return false;
  402.   }
  403.  
  404.   for (var idx in parents)
  405.   {
  406.     var parent = parents[idx];
  407.     if (parent)
  408.     {
  409.       var grandParents = parent.getParents();
  410.       if (grandParents)
  411.       {
  412.         for (var idxGP in grandParents)
  413.         {
  414.           var grandParent = grandParents[idxGP];
  415.           if (grandParent.id() == "urn:flock:topmedia")
  416.           {
  417.             this._logger.debug("belongsToTopMedia: belongs to topmedia!");
  418.             return true;
  419.           }
  420.         }
  421.       }
  422.     }
  423.   }
  424.  
  425.   return false;
  426. }
  427.  
  428.  
  429. // check if the cachedThumbnail is:
  430. // 1) not null or empty and
  431. // 2) not the loading picture
  432. // returns true if the cachedThumbnail passes the above criteria, false otherwise
  433. myworldService.prototype.isValidCachedThumbnail =
  434. function myworldService_isValidCachedThumbnail(cachedThumbnail, thumbnail)
  435. {
  436.   return cachedThumbnail && cachedThumbnail.length > 0;
  437. }
  438.  
  439.  
  440. // re-retrieve the resource from the social service to see if we can cache it ourselves and retrieve it later
  441. myworldService.prototype.reRequestResource =
  442. function myworldService_reRequestResource(thumbnail)
  443. {
  444.   this._logger.debug("BEGIN reRequestResource");
  445.   if (thumbnail)
  446.   {
  447.     var request = Cc[XMLHTTPREQUEST_CONTRACTID].createInstance(Ci.nsIXMLHttpRequest);
  448.     if (!request)
  449.     {
  450.       this._logger.debug("END reRequestResource: unable to instantiate HTTPRequest");
  451.       return;
  452.     }
  453.  
  454.     try
  455.     {
  456.       this._logger.debug("reRequestResource: HTTPRequest sent for " + thumbnail);
  457.       request.open("GET", coopObj.thumbnail, true);
  458.       request.send(null);
  459.     }
  460.     catch (ex)
  461.     {
  462.       this._logger.debug("reRequestResource: HTTPRequest failed for " + thumbnail);
  463.     }
  464.   }
  465.   this._logger.debug("END reRequestResource");
  466. }
  467.  
  468.  
  469. // requeue the resource unless the maximum attempts to retrieve the resource has been exceeded
  470. myworldService.prototype.requeueResource =
  471. function myworldService_requeueResource(elem, coopObj)
  472. {
  473.   this._logger.debug("BEGIN requeueResource");
  474.   elem.attempt++;
  475.   if (elem.attempt < MAX_RETRIEVAL_ATTEMPTS)
  476.   {
  477.     this._queue.unshift(elem);
  478.     this._logger.debug("requeueResource: " + elem.urn + " (re-added to queue successfully)");
  479.   }
  480.   else
  481.   {
  482.     // number of attempts exceeded, just set the displayed thumbnail to the original resource
  483.     coopObj.cachedThumbnail = coopObj.thumbnail;
  484.     this._logger.debug("requeueResource: " + elem.urn + " (not re-added to queue as we've already tried " + MAX_RETRIEVAL_ATTEMPTS + " times)");
  485.   }
  486.  
  487.   this._logger.debug("    number of items in queue: " + this._queue.length);
  488.   this._logger.debug("END requeueResource");
  489. }
  490.  
  491.  
  492. // check if the resource already exists in the queue to be processed
  493. // returns false if it is unique to the queue, true otherwise
  494. myworldService.prototype.queueResourceExists =
  495. function myworldService_queueResourceExists(aURN)
  496. {
  497.   this._logger.debug("BEGIN queueResourceExists: " + aURN);
  498.   if (!aURN || aURN.length < 1)
  499.   {
  500.     this._logger.debug("END queueResourceExists: true (empty string)");
  501.     return true;
  502.   }
  503.  
  504.   // ensure we don't add duplicates to the queue
  505.   // NOTE: This may become an issue if there are an excessive number of items in the queue
  506.   for (var idx = 0; idx < this._queue.length; idx++)
  507.   {
  508.     if (this._queue[idx].urn == aURN)
  509.     {
  510.       this._logger.debug("END queueResourceExists: true (already exists)");
  511.       return true;
  512.     }
  513.   }
  514.  
  515.   this._logger.debug("END queueResourceExists: false");
  516.   return false;
  517. }
  518.  
  519.  
  520. // create a new resource and add it to the service queue to be processed
  521. myworldService.prototype.createQueueResource =
  522. function myworldService_createQueueResource(aURN)
  523. {
  524.   if (aURN && aURN.length > 0)
  525.   {
  526.     // add a new url/retrieval attempts (key/value) pair to the queue
  527.     var obj = {};
  528.     obj.urn = aURN;
  529.     obj.attempt = 0;
  530.     this._logger.debug("createQueueResource:");
  531.     this._logger.debug("    URN: " + obj.urn);
  532.     this._logger.debug("    attempt: " + obj.attempt);
  533.     this._queue.unshift(obj);
  534.   }
  535. }
  536. // END flockMyworldService
  537.  
  538.  
  539. // ========== END flockMyworldService Component ============
  540.  
  541.  
  542.  
  543. // ================================================
  544. // ========== BEGIN XPCOM Module support ==========
  545. // ================================================
  546.  
  547. const ENABLE_DEBUG = false; // switch to turn off slow debug code for production
  548. function DEBUG(x) { if (ENABLE_DEBUG) debug("flockMyworldService: "+x+"\n"); }
  549.  
  550. function createModule(aParams) {
  551.   DEBUG("createModule called\n    CID: " + aParams.CID + "\n    componentName: " + aParams.componentName + "\n    contractID: " + aParams.contractID + "\n    componentClass: " + aParams.componentClass);
  552.   return {
  553.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  554.       aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  555.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  556.                                         aParams.contractID, aFileSpec,
  557.                                         aLocation, aType );
  558.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  559.         .getService(Ci.nsICategoryManager);
  560.       catMgr.addCategoryEntry( "flock-startup", aParams.componentName,
  561.                                "service,"+aParams.contractID, true, true ); 
  562.       if (!aParams.categories) { aParams.categories = []; }
  563.       for (var i = 0; i < aParams.categories.length; i++) {
  564.         var cat = aParams.categories[i];
  565.         catMgr.addCategoryEntry( cat.category, cat.entry,
  566.                                  cat.value, true, true );
  567.       }
  568.     },
  569.     getClassObject: function (aCompMgr, aCID, aIID) {
  570.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  571.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  572.       return { // Factory
  573.         createInstance: function (aOuter, aIID) {
  574.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  575.           var comp = new aParams.componentClass();
  576.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  577.           rv = comp.QueryInterface(aIID);
  578.           return rv;
  579.         }
  580.       };
  581.     },
  582.     canUnload: function (aCompMgr) { return true; }
  583.   };
  584. }
  585.  
  586. // NS Module entrypoint
  587. function NSGetModule(aCompMgr, aFileSpec) {
  588.   DEBUG("NSGetModule called");
  589.   return createModule({
  590.     componentClass: myworldService,
  591.     CID: MYWORLD_CID,
  592.     contractID: MYWORLD_CONTRACTID,
  593.     componentName: CATEGORY_COMPONENT_NAME,
  594.     implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
  595.     categories: [
  596.     ]
  597.   });
  598. }
  599.  
  600.  
  601. // ========== END XPCOM Module support ==========
  602.